package cn.jimmie.learn.viewArt.captcha.internal;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.*;
import android.graphics.drawable.Drawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import cn.jimmie.learn.viewArt.R;
import cn.jimmie.learn.viewArt.captcha.toolbox.FlashAnimationFunc;
import cn.jimmie.learn.viewArt.captcha.toolbox.PuzzleBlockFunc;


/**
 * function :
 *
 * <p> Create Time: 2018年12月09日 10:46 </p>
 * <p> @author Jimmie.Qian  </p>
 */
public class SeekCaptchaView extends AppCompatImageView {
    public static final int NONE = 0;
    // 验证阶段
    public static final int VERIFY = 1;
    // 动画阶段
    public static final int ANIM = 2;

    private final int SUCCESS_OFFSET;

    private BlockFunc mBlockFunc;
    private AnimationFunc mAnimationFunc;
    private OnSeekCaptchaVerifyListener mVerifyListener;

    // 图片缺口的路径
    private Path mBlockShadowPath = new Path();

    // 图片缺口的画笔
    private Paint mBlockShadowPaint;
    // 滑块伴随阴影的画笔
    private Paint mBlockMaskPaint;

    // 滑块图片
    private Bitmap mBlockBitmap;
    // 滑块伴随阴影的图片
    private Bitmap mBlockMaskBitmap;

    // view所处的阶段
    int mState = NONE;

    // 滑块的跟随移动的 间距
    private int mBlockOffset = 0;

    // 滑块的大小
    private int mBlockWidth;
    private int mBlockHeight;
    private int mRoundRadius;

    // 缺块的位置
    private Position mPosition = new Position();

    private int mWidth;

    public SeekCaptchaView(Context context) {
        this(context, null);
    }

    public SeekCaptchaView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SeekCaptchaView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        int dipUnit = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());

        SUCCESS_OFFSET = dipUnit * 2;
        int defaultBlockSize = dipUnit * 40;
        int defaultRoundRadius = dipUnit * 10;

//        int[] attrsArray = new int[]{android.R.attr.layout_width};
//        TypedArray sta = context.obtainStyledAttributes(attrs, attrsArray);
//        mWidth = sta.getDimensionPixelSize(0, ViewGroup.LayoutParams.MATCH_PARENT);
//        sta.recycle();

        TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.SeekCaptchaView);
        mWidth = typeArray.getDimensionPixelOffset(R.styleable.SeekCaptchaView_android_layout_width, ViewGroup.LayoutParams.MATCH_PARENT);
        mBlockWidth = typeArray.getDimensionPixelOffset(R.styleable.SeekCaptchaView_blockWidth, defaultBlockSize);
        mBlockHeight = typeArray.getDimensionPixelOffset(R.styleable.SeekCaptchaView_blockHeight, defaultBlockSize);
        mRoundRadius = typeArray.getDimensionPixelOffset(R.styleable.SeekCaptchaView_roundRadius, defaultRoundRadius);
        typeArray.recycle();

        // 设置动画效果, 如不需要, 可以在set方法中 动画效果设为null
        mAnimationFunc = new FlashAnimationFunc(this);
        // 关闭硬件加速
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    }


    // ----------------------- public method -------------------

    /**
     * 设置滑块相关 画笔, 图像的信息的策略
     * 允许用户 根据需求,设置自己的花样样式等
     * 如果不设置, 将使用默认的滑块策略
     *
     * @param blockFunction 滑块策略
     */
    @SuppressWarnings("unused")
    public SeekCaptchaView setBlockFunction(BlockFunc blockFunction) {
        mBlockFunc = blockFunction;
        return this;
    }

    /**
     * 设置动画相关的策略,
     * 包括失败动画 和 成功动画
     *
     * @param animationFunc 动画策略
     */
    @SuppressWarnings("unused")
    public SeekCaptchaView setAnimationFunc(AnimationFunc animationFunc) {
        mAnimationFunc = animationFunc;
        return this;
    }

    /**
     * 设置验证回调监听器
     */
    @SuppressWarnings("UnusedReturnValue")
    public SeekCaptchaView setVerifyListener(OnSeekCaptchaVerifyListener listener) {
        mVerifyListener = listener;
        return this;
    }

    /**
     * 触发滑块的渲染
     */
    public void render() {
        // 为了能够正确的拿到 view 的宽高等数据
        post(UIRunnable);
    }

    /**
     * 还原并且生成随机滑块信息
     */
    public void refreshBlock() {
        // 随机生成 滑块的位置信息
        mBlockFunc.generateRandomPosition(mPosition, mBlockWidth, mBlockHeight, getWidth(), getHeight());
        // 生成 滑块 路径区域
        mBlockFunc.generateBlockPath(mBlockShadowPath, mPosition, mBlockWidth, mBlockHeight);

        // 生成 滑块, 以及 它的 伴随阴影块
        mBlockBitmap = mBlockFunc.createBlockBitmap(this, mBlockShadowPath);
        mBlockMaskBitmap = mBlockBitmap.extractAlpha();

        reset();
        invalidate();
    }

    /**
     * 更新滑块位移
     *
     * @param offset 位移
     */
    public void updateOffset(int offset) {
        int unit = (int) (getWidth() - mBlockWidth * 1.5);
        if (offset > unit) offset = unit;
        mBlockOffset = offset;
        invalidate();
    }

    /**
     * 获取 可滑动的最大区域
     * 该方法必须在 View 绘制完调用,否则将取到异常值
     */
    public int getMaxRegion() {
        return mWidth - mBlockWidth;
    }

    public int getStartRegion() {
        return mBlockWidth / 3;
    }

    /**
     * 数据校验, 看是否完成验证
     */
    public void verify() {
        // 验证成功
        final boolean success = Math.abs(mBlockWidth / 3 - mPosition.x + mBlockOffset) < SUCCESS_OFFSET;
        if (mAnimationFunc == null) verifyLocal(success);
        else {
            // 如果有动画,则动画结束后 回调成功还是失败
            mAnimationFunc.setOnAnimationFinishListener(new OnAnimationFinishListener() {
                @Override
                public void onStarted() {
                    if (mVerifyListener != null)
                        mVerifyListener.onAnimationStart(success);
                }

                @Override
                public void onFinished() {
                    verifyLocal(success);
                }

            });

            // 开始失败或者成功的的动画
            ValueAnimator failAnimator = mAnimationFunc.getFailAnimator();
            failAnimator.setIntValues(mBlockOffset, 0);
            mAnimationFunc.startAnimation(success);

            // 绘制设置为动画阶段
            mState = ANIM;
        }
        invalidate();
    }

    // ------------ other method -------------------
    private void verifyLocal(boolean success) {
        if (success) {
            if (mVerifyListener != null) mVerifyListener.onVerifySuccess();
            mState = NONE;
        } else {
            if (mVerifyListener != null) mVerifyListener.onVerifyFailure();
            reset();
        }
    }

    private void renderLocal() {
        if (mBlockFunc == null)
            mBlockFunc = new PuzzleBlockFunc();

        // 动画初始化
        if (mAnimationFunc != null) mAnimationFunc.init();

        // 生成画笔
        mBlockMaskPaint = mBlockFunc.createBlockMaskPaint();
        mBlockShadowPaint = mBlockFunc.createBlockShadowPaint();

        refreshBlock();
    }

    private void reset() {
        mState = VERIFY;
        mBlockOffset = 0;
    }

    // 为了能够正常拿到View的信息
    private Runnable UIRunnable = new Runnable() {
        @Override
        public void run() {
            renderLocal();
        }
    };

    private Drawable roundDrawable(Bitmap bitmap) {
        // 等比例缩放拉伸
        float widthScale = getWidth() * 1.0f / bitmap.getWidth();
        float heightScale = getHeight() * 1.0f / bitmap.getHeight();
        Matrix matrix = new Matrix();
        matrix.setScale(widthScale, heightScale);
        Bitmap newBt = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

        // 绘制圆角
        RoundedBitmapDrawable dr = RoundedBitmapDrawableFactory.create(getResources(), newBt);
        dr.setCornerRadius(mRoundRadius);
        dr.setAntiAlias(true);

        return dr;
    }

    // ------------- life cycle -------------
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 属于渲染阶段
        if (mState == VERIFY || mState == ANIM) {
            // 绘制 缺块阴影
            canvas.drawPath(mBlockShadowPath, mBlockShadowPaint);

            // 绘制的是相对位置
            int x = mBlockWidth / 3 - mPosition.x + mBlockOffset;
//            canvas.drawBitmap(mBlockMaskBitmap, x, 0, mBlockMaskPaint);
            canvas.drawBitmap(mBlockBitmap, x, 0, null);
        }
        // 输入验证后执行动画阶段
        if (mState == ANIM) {
            if (mAnimationFunc != null) mAnimationFunc.draw(canvas);
        }
    }

    /**
     * 设置圆角
     */
    @Override
    public void setImageBitmap(final Bitmap bm) {
        if (getWidth() == 0) {
            post(new Runnable() {
                @Override
                public void run() {
                    setImageDrawable(roundDrawable(bm));
                }
            });
        } else {
            setImageDrawable(roundDrawable(bm));
        }
    }
}
